/*
* java
* Copyright (C) 2004
* 
* $Id: CL_input.java,v 1.7 2005/06/26 09:17:33 hzi Exp $
*/
/*
Copyright (C) 1997-2001 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
using System;
using Defines = Suake2.UI.Defines;
using Globals = Suake2.UI.Globals;
using Cmd = Suake2.UI.game.Cmd;
using cvar_t = Suake2.UI.game.cvar_t;
using usercmd_t = Suake2.UI.game.usercmd_t;
using Suake2.UI.qcommon;
using IN = Suake2.UI.sys.IN;
using Lib = Suake2.UI.util.Lib;
using Math3D = Suake2.UI.util.Math3D;
namespace Suake2.UI.client
{
	
	/// <summary> CL_input</summary>
	public class CL_input
	{
		private class AnonymousClassxcommand_t:xcommand_t
		{
			public override void  execute()
			{
				IN.CenterView();
			}
		}
		private class AnonymousClassxcommand_t1:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.CL_input.IN_UpDown();
			}
		}
		private class AnonymousClassxcommand_t2:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.CL_input.IN_UpUp();
			}
		}
		private class AnonymousClassxcommand_t3:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.CL_input.IN_DownDown();
			}
		}
		private class AnonymousClassxcommand_t4:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.CL_input.IN_DownUp();
			}
		}
		private class AnonymousClassxcommand_t5:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.CL_input.IN_LeftDown();
			}
		}
		private class AnonymousClassxcommand_t6:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.CL_input.IN_LeftUp();
			}
		}
		private class AnonymousClassxcommand_t7:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.CL_input.IN_RightDown();
			}
		}
		private class AnonymousClassxcommand_t8:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.CL_input.IN_RightUp();
			}
		}
		private class AnonymousClassxcommand_t9:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.CL_input.IN_ForwardDown();
			}
		}
		private class AnonymousClassxcommand_t10:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.CL_input.IN_ForwardUp();
			}
		}
		private class AnonymousClassxcommand_t11:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.CL_input.IN_BackDown();
			}
		}
		private class AnonymousClassxcommand_t12:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.CL_input.IN_BackUp();
			}
		}
		private class AnonymousClassxcommand_t13:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.CL_input.IN_LookupDown();
			}
		}
		private class AnonymousClassxcommand_t14:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.CL_input.IN_LookupUp();
			}
		}
		private class AnonymousClassxcommand_t15:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.CL_input.IN_LookdownDown();
			}
		}
		private class AnonymousClassxcommand_t16:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.CL_input.IN_LookdownUp();
			}
		}
		private class AnonymousClassxcommand_t17:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.CL_input.IN_StrafeDown();
			}
		}
		private class AnonymousClassxcommand_t18:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.CL_input.IN_StrafeUp();
			}
		}
		private class AnonymousClassxcommand_t19:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.CL_input.IN_MoveleftDown();
			}
		}
		private class AnonymousClassxcommand_t20:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.CL_input.IN_MoveleftUp();
			}
		}
		private class AnonymousClassxcommand_t21:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.CL_input.IN_MoverightDown();
			}
		}
		private class AnonymousClassxcommand_t22:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.CL_input.IN_MoverightUp();
			}
		}
		private class AnonymousClassxcommand_t23:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.CL_input.IN_SpeedDown();
			}
		}
		private class AnonymousClassxcommand_t24:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.CL_input.IN_SpeedUp();
			}
		}
		private class AnonymousClassxcommand_t25:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.CL_input.IN_AttackDown();
			}
		}
		private class AnonymousClassxcommand_t26:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.CL_input.IN_AttackUp();
			}
		}
		private class AnonymousClassxcommand_t27:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.CL_input.IN_UseDown();
			}
		}
		private class AnonymousClassxcommand_t28:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.CL_input.IN_UseUp();
			}
		}
		private class AnonymousClassxcommand_t29:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.CL_input.IN_Impulse();
			}
		}
		private class AnonymousClassxcommand_t30:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.CL_input.IN_KLookDown();
			}
		}
		private class AnonymousClassxcommand_t31:xcommand_t
		{
			public override void  execute()
			{
				jake2.client.CL_input.IN_KLookUp();
			}
		}
		
		internal static long frame_msec;
		
		internal static long old_sys_frame_time;
		
		internal static cvar_t cl_nodelta;
		
		/*
		* ===============================================================================
		* 
		* KEY BUTTONS
		* 
		* Continuous button event tracking is complicated by the fact that two
		* different input sources (say, mouse button 1 and the control key) can
		* both press the same button, but the button should only be released when
		* both of the pressing key have been released.
		* 
		* When a key event issues a button command (+forward, +attack, etc), it
		* appends its key number as a parameter to the command so it can be matched
		* up with the release.
		* 
		* state bit 0 is the current state of the key state bit 1 is edge triggered
		* on the up to down transition state bit 2 is edge triggered on the down to
		* up transition
		* 
		* 
		* Key_Event (int key, qboolean down, unsigned time);
		* 
		* +mlook src time
		* 
		* ===============================================================================
		*/
		
		internal static kbutton_t in_klook = new kbutton_t();
		
		internal static kbutton_t in_left = new kbutton_t();
		
		internal static kbutton_t in_right = new kbutton_t();
		
		internal static kbutton_t in_forward = new kbutton_t();
		
		internal static kbutton_t in_back = new kbutton_t();
		
		internal static kbutton_t in_lookup = new kbutton_t();
		
		internal static kbutton_t in_lookdown = new kbutton_t();
		
		internal static kbutton_t in_moveleft = new kbutton_t();
		
		internal static kbutton_t in_moveright = new kbutton_t();
		
		public static kbutton_t in_strafe = new kbutton_t();
		
		internal static kbutton_t in_speed = new kbutton_t();
		
		internal static kbutton_t in_use = new kbutton_t();
		
		internal static kbutton_t in_attack = new kbutton_t();
		
		internal static kbutton_t in_up = new kbutton_t();
		
		internal static kbutton_t in_down = new kbutton_t();
		
		internal static int in_impulse;
		
		internal static void  KeyDown(kbutton_t b)
		{
			int k;
			System.String c;
			
			c = Cmd.Argv(1);
			if (c.Length > 0)
				k = Lib.atoi(c);
			else
				k = - 1; // typed manually at the console for continuous down
			
			if (k == b.down[0] || k == b.down[1])
				return ; // repeating key
			
			if (b.down[0] == 0)
				b.down[0] = k;
			else if (b.down[1] == 0)
				b.down[1] = k;
			else
			{
				Com.Printf("Three keys down for a button!\n");
				return ;
			}
			
			if ((b.state & 1) != 0)
				return ; // still down
			
			// save timestamp
			c = Cmd.Argv(2);
			b.downtime = Lib.atoi(c);
			if (b.downtime == 0)
				b.downtime = Globals.sys_frame_time - 100;
			
			b.state |= 3; // down + impulse down
		}
		
		internal static void  KeyUp(kbutton_t b)
		{
			int k;
			System.String c;
			int uptime;
			
			c = Cmd.Argv(1);
			if (c.Length > 0)
				k = Lib.atoi(c);
			else
			{
				// typed manually at the console, assume for unsticking, so clear
				// all
				b.down[0] = b.down[1] = 0;
				b.state = 4; // impulse up
				return ;
			}
			
			if (b.down[0] == k)
				b.down[0] = 0;
			else if (b.down[1] == k)
				b.down[1] = 0;
			else
				return ; // key up without coresponding down (menu pass through)
			if (b.down[0] != 0 || b.down[1] != 0)
				return ; // some other key is still holding it down
			
			if ((b.state & 1) == 0)
				return ; // still up (this should not happen)
			
			// save timestamp
			c = Cmd.Argv(2);
			uptime = Lib.atoi(c);
			if (uptime != 0)
				b.msec += uptime - b.downtime;
			else
				b.msec += 10;
			
			b.state &= ~ 1; // now up
			b.state |= 4; // impulse up
		}
		
		internal static void  IN_KLookDown()
		{
			KeyDown(in_klook);
		}
		
		internal static void  IN_KLookUp()
		{
			KeyUp(in_klook);
		}
		
		internal static void  IN_UpDown()
		{
			KeyDown(in_up);
		}
		
		internal static void  IN_UpUp()
		{
			KeyUp(in_up);
		}
		
		internal static void  IN_DownDown()
		{
			KeyDown(in_down);
		}
		
		internal static void  IN_DownUp()
		{
			KeyUp(in_down);
		}
		
		internal static void  IN_LeftDown()
		{
			KeyDown(in_left);
		}
		
		internal static void  IN_LeftUp()
		{
			KeyUp(in_left);
		}
		
		internal static void  IN_RightDown()
		{
			KeyDown(in_right);
		}
		
		internal static void  IN_RightUp()
		{
			KeyUp(in_right);
		}
		
		internal static void  IN_ForwardDown()
		{
			KeyDown(in_forward);
		}
		
		internal static void  IN_ForwardUp()
		{
			KeyUp(in_forward);
		}
		
		internal static void  IN_BackDown()
		{
			KeyDown(in_back);
		}
		
		internal static void  IN_BackUp()
		{
			KeyUp(in_back);
		}
		
		internal static void  IN_LookupDown()
		{
			KeyDown(in_lookup);
		}
		
		internal static void  IN_LookupUp()
		{
			KeyUp(in_lookup);
		}
		
		internal static void  IN_LookdownDown()
		{
			KeyDown(in_lookdown);
		}
		
		internal static void  IN_LookdownUp()
		{
			KeyUp(in_lookdown);
		}
		
		internal static void  IN_MoveleftDown()
		{
			KeyDown(in_moveleft);
		}
		
		internal static void  IN_MoveleftUp()
		{
			KeyUp(in_moveleft);
		}
		
		internal static void  IN_MoverightDown()
		{
			KeyDown(in_moveright);
		}
		
		internal static void  IN_MoverightUp()
		{
			KeyUp(in_moveright);
		}
		
		internal static void  IN_SpeedDown()
		{
			KeyDown(in_speed);
		}
		
		internal static void  IN_SpeedUp()
		{
			KeyUp(in_speed);
		}
		
		internal static void  IN_StrafeDown()
		{
			KeyDown(in_strafe);
		}
		
		internal static void  IN_StrafeUp()
		{
			KeyUp(in_strafe);
		}
		
		internal static void  IN_AttackDown()
		{
			KeyDown(in_attack);
		}
		
		internal static void  IN_AttackUp()
		{
			KeyUp(in_attack);
		}
		
		internal static void  IN_UseDown()
		{
			KeyDown(in_use);
		}
		
		internal static void  IN_UseUp()
		{
			KeyUp(in_use);
		}
		
		internal static void  IN_Impulse()
		{
			in_impulse = Lib.atoi(Cmd.Argv(1));
		}
		
		/*
		* =============== CL_KeyState
		* 
		* Returns the fraction of the frame that the key was down ===============
		*/
		internal static float KeyState(kbutton_t key)
		{
			float val;
			long msec;
			
			key.state &= 1; // clear impulses
			
			msec = key.msec;
			key.msec = 0;
			
			if (key.state != 0)
			{
				// still down
				msec += Globals.sys_frame_time - key.downtime;
				key.downtime = Globals.sys_frame_time;
			}
			
			val = (float) msec / frame_msec;
			if (val < 0)
				val = 0;
			if (val > 1)
				val = 1;
			
			return val;
		}
		
		//	  ==========================================================================
		
		/*
		* ================ CL_AdjustAngles
		* 
		* Moves the local angle positions ================
		*/
		internal static void  AdjustAngles()
		{
			float speed;
			float up, down;
			
			if ((in_speed.state & 1) != 0)
				speed = Globals.cls.frametime * Globals.cl_anglespeedkey.value_Renamed;
			else
				speed = Globals.cls.frametime;
			
			if ((in_strafe.state & 1) == 0)
			{
				Globals.cl.viewangles[Defines.YAW] -= speed * Globals.cl_yawspeed.value_Renamed * KeyState(in_right);
				Globals.cl.viewangles[Defines.YAW] += speed * Globals.cl_yawspeed.value_Renamed * KeyState(in_left);
			}
			if ((in_klook.state & 1) != 0)
			{
				Globals.cl.viewangles[Defines.PITCH] -= speed * Globals.cl_pitchspeed.value_Renamed * KeyState(in_forward);
				Globals.cl.viewangles[Defines.PITCH] += speed * Globals.cl_pitchspeed.value_Renamed * KeyState(in_back);
			}
			
			up = KeyState(in_lookup);
			down = KeyState(in_lookdown);
			
			Globals.cl.viewangles[Defines.PITCH] -= speed * Globals.cl_pitchspeed.value_Renamed * up;
			Globals.cl.viewangles[Defines.PITCH] += speed * Globals.cl_pitchspeed.value_Renamed * down;
		}
		
		/*
		* ================ CL_BaseMove
		* 
		* Send the intended movement message to the server ================
		*/
		internal static void  BaseMove(usercmd_t cmd)
		{
			AdjustAngles();
			
			//memset (cmd, 0, sizeof(*cmd));
			cmd.clear();
			
			Math3D.VectorCopy(Globals.cl.viewangles, cmd.angles);
			if ((in_strafe.state & 1) != 0)
			{
				cmd.sidemove = (short) (cmd.sidemove + Globals.cl_sidespeed.value_Renamed * KeyState(in_right));
				cmd.sidemove = (short) (cmd.sidemove - Globals.cl_sidespeed.value_Renamed * KeyState(in_left));
			}
			
			cmd.sidemove = (short) (cmd.sidemove + Globals.cl_sidespeed.value_Renamed * KeyState(in_moveright));
			cmd.sidemove = (short) (cmd.sidemove - Globals.cl_sidespeed.value_Renamed * KeyState(in_moveleft));
			
			cmd.upmove = (short) (cmd.upmove + Globals.cl_upspeed.value_Renamed * KeyState(in_up));
			cmd.upmove = (short) (cmd.upmove - Globals.cl_upspeed.value_Renamed * KeyState(in_down));
			
			if ((in_klook.state & 1) == 0)
			{
				cmd.forwardmove = (short) (cmd.forwardmove + Globals.cl_forwardspeed.value_Renamed * KeyState(in_forward));
				cmd.forwardmove = (short) (cmd.forwardmove - Globals.cl_forwardspeed.value_Renamed * KeyState(in_back));
			}
			
			//
			//	   adjust for speed key / running
			//
			//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
			if (((in_speed.state & 1) ^ (int) (Globals.cl_run.value_Renamed)) != 0)
			{
				cmd.forwardmove = (short) (cmd.forwardmove * 2);
				cmd.sidemove = (short) (cmd.sidemove * 2);
				cmd.upmove = (short) (cmd.upmove * 2);
			}
		}
		
		internal static void  ClampPitch()
		{
			
			float pitch;
			
			pitch = Math3D.SHORT2ANGLE(Globals.cl.frame.playerstate.pmove.delta_angles[Defines.PITCH]);
			if (pitch > 180)
				pitch -= 360;
			
			if (Globals.cl.viewangles[Defines.PITCH] + pitch < - 360)
				Globals.cl.viewangles[Defines.PITCH] += 360; // wrapped
			if (Globals.cl.viewangles[Defines.PITCH] + pitch > 360)
				Globals.cl.viewangles[Defines.PITCH] -= 360; // wrapped
			
			if (Globals.cl.viewangles[Defines.PITCH] + pitch > 89)
				Globals.cl.viewangles[Defines.PITCH] = 89 - pitch;
			if (Globals.cl.viewangles[Defines.PITCH] + pitch < - 89)
				Globals.cl.viewangles[Defines.PITCH] = - 89 - pitch;
		}
		
		/*
		* ============== CL_FinishMove ==============
		*/
		internal static void  FinishMove(usercmd_t cmd)
		{
			int ms;
			int i;
			
			//
			//	   figure button bits
			//	
			if ((in_attack.state & 3) != 0)
				cmd.buttons |= (sbyte) (Defines.BUTTON_ATTACK);
			in_attack.state &= ~ 2;
			
			if ((in_use.state & 3) != 0)
				cmd.buttons |= (sbyte) (Defines.BUTTON_USE);
			in_use.state &= ~ 2;
			
			if (Key.anykeydown != 0 && Globals.cls.key_dest == Defines.key_game)
				cmd.buttons |= (sbyte) (Defines.BUTTON_ANY);
			
			// send milliseconds of time to apply the move
			//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
			ms = (int) (Globals.cls.frametime * 1000);
			if (ms > 250)
				ms = 100; // time was unreasonable
			cmd.msec = (sbyte) ms;
			
			ClampPitch();
			for (i = 0; i < 3; i++)
				cmd.angles[i] = (short) Math3D.ANGLE2SHORT(Globals.cl.viewangles[i]);
			
			cmd.impulse = (sbyte) SupportClass.Identity(in_impulse);
			in_impulse = 0;
			
			// send the ambient light level at the player's current position
			//UPGRADE_WARNING: Data types in Visual C# might be different.  Verify the accuracy of narrowing conversions. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1042'"
			cmd.lightlevel = (sbyte) Globals.cl_lightlevel.value_Renamed;
		}
		
		/*
		* ================= CL_CreateCmd =================
		*/
		internal static void  CreateCmd(usercmd_t cmd)
		{
			//usercmd_t cmd = new usercmd_t();
			
			frame_msec = Globals.sys_frame_time - old_sys_frame_time;
			if (frame_msec < 1)
				frame_msec = 1;
			if (frame_msec > 200)
				frame_msec = 200;
			
			// get basic movement from keyboard
			BaseMove(cmd);
			
			// allow mice or other external controllers to add to the move
			IN.Move(cmd);
			
			FinishMove(cmd);
			
			old_sys_frame_time = Globals.sys_frame_time;
			
			//return cmd;
		}
		
		/*
		* ============ CL_InitInput ============
		*/
		internal static void  InitInput()
		{
			Cmd.AddCommand("centerview", new AnonymousClassxcommand_t());
			
			Cmd.AddCommand("+moveup", new AnonymousClassxcommand_t1());
			Cmd.AddCommand("-moveup", new AnonymousClassxcommand_t2());
			Cmd.AddCommand("+movedown", new AnonymousClassxcommand_t3());
			Cmd.AddCommand("-movedown", new AnonymousClassxcommand_t4());
			Cmd.AddCommand("+left", new AnonymousClassxcommand_t5());
			Cmd.AddCommand("-left", new AnonymousClassxcommand_t6());
			Cmd.AddCommand("+right", new AnonymousClassxcommand_t7());
			Cmd.AddCommand("-right", new AnonymousClassxcommand_t8());
			Cmd.AddCommand("+forward", new AnonymousClassxcommand_t9());
			Cmd.AddCommand("-forward", new AnonymousClassxcommand_t10());
			Cmd.AddCommand("+back", new AnonymousClassxcommand_t11());
			Cmd.AddCommand("-back", new AnonymousClassxcommand_t12());
			Cmd.AddCommand("+lookup", new AnonymousClassxcommand_t13());
			Cmd.AddCommand("-lookup", new AnonymousClassxcommand_t14());
			Cmd.AddCommand("+lookdown", new AnonymousClassxcommand_t15());
			Cmd.AddCommand("-lookdown", new AnonymousClassxcommand_t16());
			Cmd.AddCommand("+strafe", new AnonymousClassxcommand_t17());
			Cmd.AddCommand("-strafe", new AnonymousClassxcommand_t18());
			Cmd.AddCommand("+moveleft", new AnonymousClassxcommand_t19());
			Cmd.AddCommand("-moveleft", new AnonymousClassxcommand_t20());
			Cmd.AddCommand("+moveright", new AnonymousClassxcommand_t21());
			Cmd.AddCommand("-moveright", new AnonymousClassxcommand_t22());
			Cmd.AddCommand("+speed", new AnonymousClassxcommand_t23());
			Cmd.AddCommand("-speed", new AnonymousClassxcommand_t24());
			Cmd.AddCommand("+attack", new AnonymousClassxcommand_t25());
			Cmd.AddCommand("-attack", new AnonymousClassxcommand_t26());
			Cmd.AddCommand("+use", new AnonymousClassxcommand_t27());
			Cmd.AddCommand("-use", new AnonymousClassxcommand_t28());
			Cmd.AddCommand("impulse", new AnonymousClassxcommand_t29());
			Cmd.AddCommand("+klook", new AnonymousClassxcommand_t30());
			Cmd.AddCommand("-klook", new AnonymousClassxcommand_t31());
			
			cl_nodelta = Cvar.Get("cl_nodelta", "0", 0);
		}
		
		//UPGRADE_NOTE: Final was removed from the declaration of 'buf '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
		private static readonly sizebuf_t buf = new sizebuf_t();
		//UPGRADE_NOTE: Final was removed from the declaration of 'data '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
		private static readonly sbyte[] data = new sbyte[128];
		//UPGRADE_NOTE: Final was removed from the declaration of 'nullcmd '. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1003'"
		private static readonly usercmd_t nullcmd = new usercmd_t();
		/*
		* ================= CL_SendCmd =================
		*/
		internal static void  SendCmd()
		{
			int i;
			usercmd_t cmd, oldcmd;
			int checksumIndex;
			
			// build a command even if not connected
			
			// save this command off for prediction
			i = Globals.cls.netchan.outgoing_sequence & (Defines.CMD_BACKUP - 1);
			cmd = Globals.cl.cmds[i];
			Globals.cl.cmd_time[i] = (int) Globals.cls.realtime; // for netgraph
			// ping calculation
			
			// fill the cmd
			CreateCmd(cmd);
			
			Globals.cl.cmd.set_Renamed(cmd);
			
			if (Globals.cls.state == Defines.ca_disconnected || Globals.cls.state == Defines.ca_connecting)
				return ;
			
			if (Globals.cls.state == Defines.ca_connected)
			{
				if (Globals.cls.netchan.message.cursize != 0 || Globals.curtime - Globals.cls.netchan.last_sent > 1000)
					Netchan.Transmit(Globals.cls.netchan, 0, new sbyte[0]);
				return ;
			}
			
			// send a userinfo update if needed
			if (Globals.userinfo_modified)
			{
				CL.FixUpGender();
				Globals.userinfo_modified = false;
				MSG.WriteByte(Globals.cls.netchan.message, Defines.clc_userinfo);
				MSG.WriteString(Globals.cls.netchan.message, Cvar.Userinfo());
			}
			
			SZ.Init(buf, data, data.Length);
			
			if (cmd.buttons != 0 && Globals.cl.cinematictime > 0 && !Globals.cl.attractloop && Globals.cls.realtime - Globals.cl.cinematictime > 1000)
			{
				// skip
				// the
				// rest
				// of
				// the
				// cinematic
				SCR.FinishCinematic();
			}
			
			// begin a client move command
			MSG.WriteByte(buf, Defines.clc_move);
			
			// save the position for a checksum byte
			checksumIndex = buf.cursize;
			MSG.WriteByte(buf, 0);
			
			// let the server know what the last frame we
			// got was, so the next message can be delta compressed
			if (cl_nodelta.value_Renamed != 0.0f || !Globals.cl.frame.valid || Globals.cls.demowaiting)
				MSG.WriteLong(buf, - 1);
			// no compression
			else
				MSG.WriteLong(buf, Globals.cl.frame.serverframe);
			
			// send this and the previous cmds in the message, so
			// if the last packet was dropped, it can be recovered
			i = (Globals.cls.netchan.outgoing_sequence - 2) & (Defines.CMD_BACKUP - 1);
			cmd = Globals.cl.cmds[i];
			//memset (nullcmd, 0, sizeof(nullcmd));
			nullcmd.clear();
			
			MSG.WriteDeltaUsercmd(buf, nullcmd, cmd);
			oldcmd = cmd;
			
			i = (Globals.cls.netchan.outgoing_sequence - 1) & (Defines.CMD_BACKUP - 1);
			cmd = Globals.cl.cmds[i];
			
			MSG.WriteDeltaUsercmd(buf, oldcmd, cmd);
			oldcmd = cmd;
			
			i = (Globals.cls.netchan.outgoing_sequence) & (Defines.CMD_BACKUP - 1);
			cmd = Globals.cl.cmds[i];
			
			MSG.WriteDeltaUsercmd(buf, oldcmd, cmd);
			
			// calculate a checksum over the move commands
			buf.data[checksumIndex] = Com.BlockSequenceCRCByte(buf.data, checksumIndex + 1, buf.cursize - checksumIndex - 1, Globals.cls.netchan.outgoing_sequence);
			
			//
			// deliver the message
			//
			Netchan.Transmit(Globals.cls.netchan, buf.cursize, buf.data);
		}
	}
}